#include "BigInt.h"
#include <sstream>
using namespace std;
using namespace APIAL;

/*
	Parses a string of binary characters ('0' and '1').
	Size and capacity are set as appropriate.
	@positive True if the number is to be considered positive
*/
int BigInt::ParseBinaryString(const string &binary, bool positive_) {
	// Check for valid binary string
	if (binary.find_first_not_of("01") != string::npos)
	{
		return -1;
	}

	// Empty string
	if (binary.size() <= 0)
	{
		this->size = 0; 
		return 0;
	}

	// Set size/capacity/positive/normalized
    this->size = (binary.size()+nonCarryBits-1)/nonCarryBits;
    if (capacity < size) {
        delete [] chunks;
        chunks = new chunkType[this->size];
        capacity = size;
    }
    this->positive = positive_;
	this->normalized = true;

    // Clear chunks
    for (int i = 0; i < (int)size; i++) {
        chunks[i] = 0;
    }

    // Parse binary string
    for (int i = 0; i < (int)binary.size(); i++) {
        // Anything that is not a '0' is a '1'.
        char input = binary[binary.size()-1-i];

        if (input == '1')
		{
            this->chunks[i/this->nonCarryBits] |= (1 << i%this->nonCarryBits);
        }
    }

	return 0;
}

/*
	Parses a string of hexadecimal characters ('0'-'9', 'a'-'f', 'A'-'F').
	Size and capacity are set as appropriate.
	@positive True if the number is to be considered positive
*/
int BigInt::ParseHexadecimalString(const string &hexadecimal, bool positive_) {
	// Check for valid hexadecimal string
	if (hexadecimal.find_first_not_of("0123456789AaBbCcDdEeFf") != string::npos)
	{
		return -1;
	}

	// Empty string
	if (hexadecimal.size() <= 0) {
		this->size = 0;
		return 0;
	}

	// Set size/capacity/normalized
    this->size = ((hexadecimal.size()*4)+nonCarryBits-1)/nonCarryBits;
	if (capacity < size) {
        delete [] chunks;
        chunks = new chunkType[this->size];
        capacity = size;
    }
    this->positive = positive_;
	this->normalized = true;

    // Clear chunks
    for (int i = 0; i < this->size; i++) {
        chunks[i] = 0;
    }

    // Parse hexadecimal string
    for (int i = 0; i < (int)hexadecimal.size(); i++) {
        // Get the hexadecimal bits
        char input = toupper(hexadecimal[hexadecimal.size()-1-i]);
        char bits = 0;
        if (input <= '9' && input >= '0') {
            bits = input-'0';
        } else {
            bits = input-'A'+10;
        }

        // Set the bits
        for (int j = 0; j < 4; j++) {
            int bit = ((bits >> j) & 0x01);
			int bitNum = i*4+j;
            int chunkIndex = bitNum/this->nonCarryBits;
            int chunkBitIndex = bitNum%this->nonCarryBits;
            this->chunks[chunkIndex] |= (bit << chunkBitIndex);
        }
    }

	// Possibly overestimated the size (the top 3 bits of the most significant
	//  hexadecimal digit could be 0.
	if (this->chunks[size-1] == 0) {
		size--;
	}

	return 0;
}

std::string APIAL::PrintMagnitudeHexadecimal(BigInt *bi)
{
	stringstream number;

	BigInt::normalize(*bi);

	// if the most significant chunk is zero, decrement size
	while(bi->size > 0 && 0 == bi->chunks[bi->size-1])
	{
		bi->size--;
	}

	if (bi->size == 0)
	{
		number << "0";
	}
	else
	{
		// current decimal value of hex digit
		int hex_value = 0;

		// number of bits needed to extract before we can convert hex_value to a hex digit
		int bits_needed = 0;

		// get number of bits to shift so that the MSB of the chunk is the first nonCarryBit
		int unused_bits = (sizeof(BigInt::chunkType)*8) - bi->nonCarryBits;

		// special case for most significant chunk
		BigInt::chunkType temp = bi->chunks[bi->size - 1];
		temp <<= unused_bits;
		int i = 0;

		while(temp == ((temp << 1) >> 1))
		{
			// MSB is 0
			temp <<= 1;
			i++;
		}

		// calculate total bits needed to represent number
		int total_bits = (bi->nonCarryBits - i) + (bi->nonCarryBits * (bi->size - 1));

		bits_needed = total_bits % 4;
		if (bits_needed == 0)
		{
			bits_needed = 4;
		}

		for (; i < bi->nonCarryBits; i++)
		{
			bits_needed--;

			if (temp != ((temp << 1) >> 1))
			{
				// MSB is 1
				hex_value += 1 << bits_needed;
			}

			if (bits_needed <= 0)
			{
				if (hex_value < 10)
				{
					number << (char)('0' + hex_value);
				}
				else
				{
					number << (char)('A'+ (hex_value - 10));
				}

				bits_needed = 4;
				hex_value = 0;
			}

			temp <<= 1;
		}


		for (int j = bi->size - 2; j >= 0; j--)
		{
			BigInt::chunkType temp = bi->chunks[j];
			temp <<= unused_bits;

			for (int k = 0; k < bi->nonCarryBits; k++)
			{
				bits_needed--;

				if (temp != ((temp << 1) >> 1))
				{
					// MSB is 1
					hex_value += 1 << bits_needed;
				}

				if (bits_needed <= 0)
				{
					if (hex_value < 10)
					{
						number << (char)('0' + hex_value);
					}
					else
					{
						number << (char)('A'+ (hex_value - 10));
					}

					bits_needed = 4;
					hex_value = 0;
				}

				temp <<= 1;
			}
		}
	}

	return number.str();
}

std::string APIAL::PrintMagnitudeBinary(BigInt *bi)
{
	stringstream number;

	BigInt::normalize(*bi);

	// if the most significant chunk is zero, decrement size
	while(bi->size > 0 && 0 == bi->chunks[bi->size-1])
	{
		bi->size--;
	}

	if (bi->size == 0)
	{
		number << "0";
	}
	else
	{
		// get number of bits to shift so that the MSB of the chunk is the first nonCarryBit
		int unused_bits = (sizeof(BigInt::chunkType)*8) - bi->nonCarryBits;

		// special case for most significant chunk
		BigInt::chunkType temp = bi->chunks[bi->size - 1];
		temp <<= unused_bits;
		int i = 0;

		while(temp == ((temp << 1) >> 1))
		{
			// MSB is 0
			temp <<= 1;
			i++;
		}

		for (; i < bi->nonCarryBits; i++)
		{
			if (temp == ((temp << 1) >> 1))
			{
				// MSB is 0
				number << "0";
			}
			else
			{
				// MSB is 1
				number << "1";
			}

			temp <<= 1;
		}


		for (int j = bi->size - 2; j >= 0; j--)
		{
			BigInt::chunkType temp = bi->chunks[j];
			temp <<= unused_bits;

			for (int k = 0; k < bi->nonCarryBits; k++)
			{
				if (temp == ((temp << 1) >> 1))
				{
					// MSB is 0
					number << "0";
				}
				else
				{
					// MSB is 1
					number << "1";
				}

				temp <<= 1;
			}
		}
	}

	return number.str();
}